iT邦幫忙

2023 iThome 鐵人賽

DAY 8
0

Effective Kotlin : Item 5: Specify your expectations on arguments and state

契約式設計 - Design By Contract

昨天說的 @Nullable 是契約式設計(Design By Contract)的一環,除了可空性外外,函式或介面參數的合法性是更需要被定義及申明的,這樣被呼叫時開發者可以明確的知道副作用。因此 Kotlin 作為一個現代化程式語言有自帶契約式設計的支持,可以用 require / check / assert 明訂契約

https://ithelp.ithome.com.tw/upload/images/20230914/20135701FYjLOmcsUC.png

指定你對參數和狀態的期望

當對於參數的範圍,狀態有期望時,應盡早宣告它們。在 Kotlin 中,主要使用以下方法進行此操作:

  • require : 指定對傳入參數的期望
  • check 區塊 : 指定對狀態的期望
  • assert 區塊 : 檢查某事是否為真。在 JVM 中,此類檢查只會在測試模式下進行評估
  • 使用 Elvis 運算子進行 return 或 throw

以下為 stack pop 的範例

// Part of Stack<T>
fun pop(num: Int = 1): List<T> {
    require(num <= size) {
        "Cannot remove more elements than current size"
    }
    check(isOpen) { "Cannot pop from closed stack" }
    val ret = collection.take(num)
    collection = collection.drop(num)
    assert(ret.size == num)
    return ret
}

當條件不符時,require/check 會拋出不同的 exception。由 exception 的名稱也可以看出 require 是要檢查傳入的參數狀態, check 是檢查內部或處理後的狀態。

  • require(Boolean) 丟出 IllegalArgumentException
  • check(Boolean) 丟出 IllegalStateException
  • assert(Boolean) 丟出 AssertionError

為了使 assert 起作用,需要在 JVM 中啟用斷言功能(-ea)。如果不啟用,assert 將不會進行任何操作

checkNotNull, requireNotNull 的解 null 魔力

延伸的還有 checkNotNull, requireNotNull

requireNotNull

檢查給定的值是否為 null,如果是 null,則拋出 IllegalArgumentException。

val nonNullValue = requireNotNull(possibleNullValue) { "Value must not be null" }

checkNotNull

檢查給定的值是否為 null,如果是 null,則拋出 IllegalStateException。

val nonNullValue = checkNotNull(possibleNullValue) { "Value must not be null" }

Nullability and smart casting

除了作用相似外。在通過 checkNotNull, requireNotNull 的參數 Kotlin 就會自動視為非空。如下的 validateEmail 本來不能傳入 email:String? 但因為上面通過了requireNotNull(person.email),所以 Kotlin 知道那不是空的。就可以直接使用

class Person(val email: String?)

fun validateEmail(email: String) { /*...*/
}

fun sendEmail(person: Person, text: String) {
  val email = requireNotNull(person.email)
  validateEmail(email)
  //...
}

fun sendEmail(person: Person, text: String) {
  requireNotNull(person.email)
  validateEmail(person.email)
  //...
}

結語

check 函數的工作方式與 require 類似,但當所述的期望未被滿足時,它會拋出 IllegalStateException。它檢查某個狀態是否正確,通常在 require 區塊之後。但是,有些狀態期望是局部的,check 可以稍後使用。

每日一推 (G)I-DLE

TOMBOY

Yes


上一篇
D07: 料敵從寬, 在邊界就處理外界來的 nullable
下一篇
D09: 當回傳值可能有副作用時,回傳 null 或封裝後的 Result Type
系列文
讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言